package com.yami.shop.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.dto.BlockchainShopDTO;
import com.yami.shop.bean.enums.BrandType;
import com.yami.shop.bean.enums.ProdStatusEnums;
import com.yami.shop.bean.enums.SigningStatus;
import com.yami.shop.bean.event.SigningBlockchainInvalidEvent;
import com.yami.shop.bean.model.Blockchain;
import com.yami.shop.bean.model.BlockchainShop;
import com.yami.shop.bean.model.NftManage;
import com.yami.shop.bean.model.ShopAuditing;
import com.yami.shop.bean.vo.BlockchainShopVO;
import com.yami.shop.bean.vo.BrandShopVO;
import com.yami.shop.bean.vo.BrandSigningVO;
import com.yami.shop.common.constants.CacheNames;
import com.yami.shop.common.constants.ProductCacheNames;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.common.util.RedisUtil;
import com.yami.shop.dao.BlockchainShopMapper;
import com.yami.shop.dao.ProductMapper;
import com.yami.shop.dao.ShopAuditingMapper;
import com.yami.shop.service.BlockchainShopService;
import lombok.AllArgsConstructor;
import org.springframework.aop.framework.AopContext;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Author lth
 * @Date 2021/7/28 10:26
 */
@Service
@AllArgsConstructor
public class BlockchainShopServiceImpl extends ServiceImpl<BlockchainShopMapper, BlockchainShop> implements BlockchainShopService {

    private final BlockchainShopMapper blockchainShopMapper;
    private final ProductMapper productMapper;
    private final ApplicationEventPublisher applicationEventPublisher;
    private final ShopAuditingMapper shopAuditingMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void signingBlockchain(List<BlockchainShop> blockchainShopList, Long shopId, Boolean isPlatform) {
        checkShop(shopId);
        // 处理链平台扣率问题
        this.dealWithSigningBlockchainInfo(blockchainShopList, shopId, isPlatform, true);
        // 删除已签约的平台链平台信息
        blockchainShopMapper.delete(Wrappers.lambdaQuery(BlockchainShop.class).eq(BlockchainShop::getShopId, shopId));
        // 重新插入
        if (CollUtil.isNotEmpty(blockchainShopList)) {
            blockchainShopMapper.saveBatch(blockchainShopList, shopId, isPlatform ? SigningStatus.SUCCESS.value() : SigningStatus.PENDING_REVIEW.value());
        }
        // 更新缓存
        this.removeCacheByShopIds(Collections.singletonList(shopId));
    }

    @Override
    public void insertBatchByShopId(List<BlockchainShop> BlockchainShopList, Long shopId, Integer status) {
        if (CollUtil.isEmpty(BlockchainShopList)) {
            return;
        }
        BlockchainShopList.forEach(a -> {
            a.setIsSynchronization(0);
            a.setIsCoin(0);
        });
        blockchainShopMapper.saveBatch(BlockchainShopList, shopId, status);
        removeCacheByShopIds(Collections.singletonList(shopId));
    }

    @Override
    @Cacheable(cacheNames = ProductCacheNames.LIST_SIGNING_BLOCKCHAIN_BY_SHOP_ID, key = "#shopId")
    public List<BlockchainShopVO> listSigningBlockchainAndLang(Long shopId) {
        return blockchainShopMapper.listSigningBlockchainByShopId(shopId);
    }

    @Override
    public List<BlockchainShopVO> listSigningBlockchainByShopId(Long shopId) {
        BlockchainShopServiceImpl blockchainShopService = (BlockchainShopServiceImpl) AopContext.currentProxy();
        return blockchainShopService.listSigningBlockchainAndLang(shopId);
    }

    @Override
    @Cacheable(cacheNames = ProductCacheNames.LIST_AVAILABLE_SIGNING_BLOCKCHAIN_BY_SHOP_ID, key = "#shopId")
    public List<BlockchainShopVO> listAvailableSigningBlockchainLang(Long shopId) {
        return blockchainShopMapper.listSigningBlockchainByShopIdAndStatus(shopId, 1);
    }

    @Override
    public List<BlockchainShopVO> listAvailableSigningBlockchain(Long shopId) {
        BlockchainShopServiceImpl blockchainShopService = (BlockchainShopServiceImpl) AopContext.currentProxy();
        return blockchainShopService.listAvailableSigningBlockchainLang(shopId);
    }

    @Override
    public List<BlockchainShop> listRateByShopId(Long shopId,Long blockchainId) {
        return blockchainShopMapper.listDiscountByShopIdAndBlockchainId(shopId,blockchainId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteByBlockchainId(Long blockchainId) {
        List<Long> shopIdList = blockchainShopMapper.listShopIdByBlockchainIds(Collections.singletonList(blockchainId));
        // 删除与该链平台id关联的信息
        blockchainShopMapper.delete(Wrappers.lambdaQuery(BlockchainShop.class).eq(BlockchainShop::getBlockchainId, blockchainId));
        // 清除缓存
        removeCacheByShopIds(shopIdList);
    }

    @Override
    public void removeCacheByChangeBlockchainIds(List<Long> blockchainIds) {
        if (CollUtil.isEmpty(blockchainIds)) {
            return;
        }
        List<Long> shopIdList = blockchainShopMapper.listShopIdByBlockchainIds(blockchainIds);
        this.removeCacheByShopIds(shopIdList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeStatusByShopIdAndStatus(Long shopId, Integer oldStatus, Integer newStatus) {
        blockchainShopMapper.changeStatusByShopIdAndStatus(shopId, oldStatus, newStatus);
        removeCacheByShopIds(Collections.singletonList(shopId));
    }

    /**
     * 处理链平台扣率问题
     * @param BlockchainShopDTOList
     * @param shopId
     * @param isPlatform
     * @param isApplyShop
     */
    private void dealWithSigningBlockchainInfo(List<BlockchainShop> BlockchainShopDTOList, Long shopId, Boolean isPlatform, Boolean isApplyShop) {
        // 查找以前签约的链平台信息列表
        List<BlockchainShop> oldBlockchainShopList = blockchainShopMapper.selectList(Wrappers.lambdaQuery(BlockchainShop.class).eq(BlockchainShop::getShopId, shopId).orderByAsc(BlockchainShop::getBlockchainId));
        if (!isPlatform) {
            for (BlockchainShop BlockchainShop : BlockchainShopDTOList) {
                // 查找是否存在
                int index = this.getIndex(BlockchainShop.getBlockchainId(), oldBlockchainShopList);
                if (index == -1) {
                    BlockchainShop.setDiscount(null);
                } else {
                    BlockchainShop.setBlockchainShopId(oldBlockchainShopList.get(index).getBlockchainShopId());
                    BlockchainShop.setDiscount(oldBlockchainShopList.get(index).getDiscount());
                    oldBlockchainShopList.remove(index);
                }
            }
        } else {
            for (BlockchainShop BlockchainShop : BlockchainShopDTOList) {
                // 查找是否存在
                int index = this.getIndex(BlockchainShop.getBlockchainId(), oldBlockchainShopList);
                if (index != -1) {
                    BlockchainShop.setBlockchainShopId(oldBlockchainShopList.get(index).getBlockchainShopId());
                    oldBlockchainShopList.remove(index);
                }
            }
        }
        if (!isApplyShop) {
            // 如果是开店时调用则不用执行下面方法
            return;
        }
        this.dealWithDeleteBlockchainList(oldBlockchainShopList, shopId);
    }

    /**
     * 根据链平台id查找该链平台在列表中的索引
     *
     * @param blockchainId       链平台id
     * @param BlockchainShopList categoryId正序的列表
     * @return
     */
    private int getIndex(Long blockchainId, List<BlockchainShop> BlockchainShopList) {
        if (Objects.isNull(blockchainId) || CollUtil.isEmpty(BlockchainShopList)) {
            return -1;
        }
        int leftIndex = 0;
        int rightIndex = BlockchainShopList.size() - 1;
        int midIndex;
        while (leftIndex <= rightIndex) {
            midIndex = leftIndex + ((rightIndex - leftIndex) >>> 1);
            Long currentId = BlockchainShopList.get(midIndex).getBlockchainId();
            if (Objects.equals(blockchainId, currentId)) {
                return midIndex;
            }
            if (currentId > blockchainId) {
                rightIndex = midIndex - 1;
            } else {
                leftIndex = midIndex + 1;
            }
        }
        return -1;
    }

    /**
     * 处理签约失效的链平台
     *
     * @param BlockchainShopList
     * @param shopId
     */
    private void dealWithDeleteBlockchainList(List<BlockchainShop> BlockchainShopList, Long shopId) {
        List<Long> ids = BlockchainShopList.stream().map(BlockchainShop::getBlockchainId).collect(Collectors.toList());
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        productMapper.offlineProdByChainCodesIdAndShopId(ids, ProdStatusEnums.SHOP_OFFLINE.getValue(), shopId);
    }

    @Override
    public void removeCacheByShopIds(List<Long> shopIds) {
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        List<String> keyList = new ArrayList<>();
        shopIds.forEach(shopId -> {
            keyList.add(ProductCacheNames.LIST_AVAILABLE_SIGNING_BLOCKCHAIN_BY_SHOP_ID + CacheNames.UNION + shopId);
            keyList.add(ProductCacheNames.LIST_SIGNING_BLOCKCHAIN_BY_SHOP_ID + CacheNames.UNION + shopId);
        });
        RedisUtil.deleteBatch(keyList);
    }

    @Override
    public IPage<BlockchainShopVO> pageSigningInfo(PageParam<BlockchainShopVO> page, BlockchainShopDTO BlockchainShop) {
        return blockchainShopMapper.pageSigningInfo(page, BlockchainShop);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long shopId, Long blockchainId) {
        int count = blockchainShopMapper.delete(Wrappers.lambdaQuery(BlockchainShop.class).eq(BlockchainShop::getShopId, shopId).eq(BlockchainShop::getBlockchainId, blockchainId));
        if (count != 1) {
            throw new YamiShopBindException("删除失败");
        }
        // 清除签约链平台的缓存
        this.removeCacheByShopIds(Collections.singletonList(shopId));
        // 发送签约链平台失效事件
        applicationEventPublisher.publishEvent(new SigningBlockchainInvalidEvent(blockchainId, shopId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDiscount(Long shopId, Long blockchainId, Double discount) {
        boolean update = this.update(Wrappers.lambdaUpdate(BlockchainShop.class).set(BlockchainShop::getDiscount, discount).eq(BlockchainShop::getShopId, shopId).eq(BlockchainShop::getBlockchainId, blockchainId));
        if (!update) {
            throw new YamiShopBindException("更新失败");
        }
        this.removeCacheByShopIds(Collections.singletonList(shopId));
    }

    @Override
    public List<BlockchainShopVO> listSigningByShopId(Long shopId, Integer status) {
        // 查询平台品牌签约列表
        List<BlockchainShopVO> platformBlockchainList = blockchainShopMapper.listSigningBlockchainByShopIdAndStatus(shopId, status);
        return platformBlockchainList;
    }

    @Override
    public IPage<BlockchainShopVO> pageSigningByShopId(PageParam<BlockchainShopVO> page, BlockchainShopDTO blockchainShopDTO) {
        IPage<BlockchainShopVO> res = blockchainShopMapper.pageSigningInfo(page, blockchainShopDTO);
        return res;
    }

    @Override
    public List<BlockchainShopVO> listByIsCoin(Long shopId, Long applyId) {
        return blockchainShopMapper.listByIsCoin(shopId, applyId);
    }

    @Override
    public void updateSynchronizationStatus(Long blockchainShopId, Integer isSynchronization) {

        BlockchainShop blockchainShop = new BlockchainShop();
        blockchainShop.setBlockchainShopId(blockchainShopId);
        blockchainShop.setIsSynchronization(isSynchronization);
        blockchainShopMapper.updateById(blockchainShop);

        BlockchainShop shop = blockchainShopMapper.selectById(blockchainShopId);
        if (null != shop) {
            List<String> keyList = new ArrayList<>();
            keyList.add(ProductCacheNames.LIST_AVAILABLE_SIGNING_BLOCKCHAIN_BY_SHOP_ID + CacheNames.UNION + shop.getShopId());
            keyList.add(ProductCacheNames.LIST_SIGNING_BLOCKCHAIN_BY_SHOP_ID + CacheNames.UNION + shop.getShopId());
            RedisUtil.deleteBatch(keyList);
        }
    }

    private void checkShop(Long shopId) {
        long count = shopAuditingMapper.selectCount(Wrappers.lambdaQuery(ShopAuditing.class).eq(ShopAuditing::getShopId, shopId).eq(ShopAuditing::getStatus, 0));
        if (count != 0) {
            throw new YamiShopBindException("yami.product.shop.AUDIT");
        }
    }
}
